// Copyright (c) 2022. Eritque arcus and contributors.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or any later version(in your opinion).
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#pragma once

#include <limits>      // numeric_limits
#include <tuple>       // tuple
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility>     // declval

#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/call_std/begin.hpp>
#include <nlohmann/detail/meta/call_std/end.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/json_fwd.hpp>

NLOHMANN_JSON_NAMESPACE_BEGIN
/*!
@brief detail namespace with internal helper functions

This namespace collects functions that should not be exposed,
implementations of some @ref basic_json methods, and meta-programming helpers.

@since version 2.1.0
*/
namespace detail {

    /////////////
    // helpers //
    /////////////

    // Note to maintainers:
    //
    // Every trait in this file expects a non CV-qualified type.
    // The only exceptions are in the 'aliases for detected' section
    // (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
    //
    // In this case, T has to be properly CV-qualified to constraint the function arguments
    // (e.g. to_json(BasicJsonType&, const T&))

    template<typename>
    struct is_basic_json : std::false_type {};

    NLOHMANN_BASIC_JSON_TPL_DECLARATION
    struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};

    // used by exceptions create() member functions
    // true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
    // false_type otherwise
    template<typename BasicJsonContext>
    struct is_basic_json_context : std::integral_constant<bool,
                                                          is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value || std::is_same<BasicJsonContext, std::nullptr_t>::value> {};

    //////////////////////
    // json_ref helpers //
    //////////////////////

    template<typename>
    class json_ref;

    template<typename>
    struct is_json_ref : std::false_type {};

    template<typename T>
    struct is_json_ref<json_ref<T>> : std::true_type {};

    //////////////////////////
    // aliases for detected //
    //////////////////////////

    template<typename T>
    using mapped_type_t = typename T::mapped_type;

    template<typename T>
    using key_type_t = typename T::key_type;

    template<typename T>
    using value_type_t = typename T::value_type;

    template<typename T>
    using difference_type_t = typename T::difference_type;

    template<typename T>
    using pointer_t = typename T::pointer;

    template<typename T>
    using reference_t = typename T::reference;

    template<typename T>
    using iterator_category_t = typename T::iterator_category;

    template<typename T, typename... Args>
    using to_json_function = decltype(T::to_json(std::declval<Args>()...));

    template<typename T, typename... Args>
    using from_json_function = decltype(T::from_json(std::declval<Args>()...));

    template<typename T, typename U>
    using get_template_function = decltype(std::declval<T>().template get<U>());

    // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
    template<typename BasicJsonType, typename T, typename = void>
    struct has_from_json : std::false_type {};

    // trait checking if j.get<T> is valid
    // use this trait instead of std::is_constructible or std::is_convertible,
    // both rely on, or make use of implicit conversions, and thus fail when T
    // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
    template<typename BasicJsonType, typename T>
    struct is_getable {
        static constexpr bool value = is_detected<get_template_function, const BasicJsonType &, T>::value;
    };

    template<typename BasicJsonType, typename T>
    struct has_from_json<BasicJsonType, T, enable_if_t<!is_basic_json<T>::value>> {
        using serializer = typename BasicJsonType::template json_serializer<T, void>;

        static constexpr bool value =
                is_detected_exact<void, from_json_function, serializer,
                                  const BasicJsonType &, T &>::value;
    };

    // This trait checks if JSONSerializer<T>::from_json(json const&) exists
    // this overload is used for non-default-constructible user-defined-types
    template<typename BasicJsonType, typename T, typename = void>
    struct has_non_default_from_json : std::false_type {};

    template<typename BasicJsonType, typename T>
    struct has_non_default_from_json<BasicJsonType, T, enable_if_t<!is_basic_json<T>::value>> {
        using serializer = typename BasicJsonType::template json_serializer<T, void>;

        static constexpr bool value =
                is_detected_exact<T, from_json_function, serializer,
                                  const BasicJsonType &>::value;
    };

    // This trait checks if BasicJsonType::json_serializer<T>::to_json exists
    // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
    template<typename BasicJsonType, typename T, typename = void>
    struct has_to_json : std::false_type {};

    template<typename BasicJsonType, typename T>
    struct has_to_json<BasicJsonType, T, enable_if_t<!is_basic_json<T>::value>> {
        using serializer = typename BasicJsonType::template json_serializer<T, void>;

        static constexpr bool value =
                is_detected_exact<void, to_json_function, serializer, BasicJsonType &,
                                  T>::value;
    };

    template<typename T>
    using detect_key_compare = typename T::key_compare;

    template<typename T>
    struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};

    // obtains the actual object key comparator
    template<typename BasicJsonType>
    struct actual_object_comparator {
        using object_t = typename BasicJsonType::object_t;
        using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
        using type = typename std::conditional<has_key_compare<object_t>::value,
                                               typename object_t::key_compare, object_comparator_t>::type;
    };

    template<typename BasicJsonType>
    using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;

    ///////////////////
    // is_ functions //
    ///////////////////

    // https://en.cppreference.com/w/cpp/types/conjunction
    template<class...>
    struct conjunction : std::true_type {};
    template<class B>
    struct conjunction<B> : B {};
    template<class B, class... Bn>
    struct conjunction<B, Bn...>
        : std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};

    // https://en.cppreference.com/w/cpp/types/negation
    template<class B>
    struct negation : std::integral_constant<bool, !B::value> {};

    // Reimplementation of is_constructible and is_default_constructible, due to them being broken for
    // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
    // This causes compile errors in e.g. clang 3.5 or gcc 4.9.
    template<typename T>
    struct is_default_constructible : std::is_default_constructible<T> {};

    template<typename T1, typename T2>
    struct is_default_constructible<std::pair<T1, T2>>
        : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};

    template<typename T1, typename T2>
    struct is_default_constructible<const std::pair<T1, T2>>
        : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};

    template<typename... Ts>
    struct is_default_constructible<std::tuple<Ts...>>
        : conjunction<is_default_constructible<Ts>...> {};

    template<typename... Ts>
    struct is_default_constructible<const std::tuple<Ts...>>
        : conjunction<is_default_constructible<Ts>...> {};


    template<typename T, typename... Args>
    struct is_constructible : std::is_constructible<T, Args...> {};

    template<typename T1, typename T2>
    struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};

    template<typename T1, typename T2>
    struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};

    template<typename... Ts>
    struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};

    template<typename... Ts>
    struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};


    template<typename T, typename = void>
    struct is_iterator_traits : std::false_type {};

    template<typename T>
    struct is_iterator_traits<iterator_traits<T>> {
    private:
        using traits = iterator_traits<T>;

    public:
        static constexpr auto value =
                is_detected<value_type_t, traits>::value &&
                is_detected<difference_type_t, traits>::value &&
                is_detected<pointer_t, traits>::value &&
                is_detected<iterator_category_t, traits>::value &&
                is_detected<reference_t, traits>::value;
    };

    template<typename T>
    struct is_range {
    private:
        using t_ref = typename std::add_lvalue_reference<T>::type;

        using iterator = detected_t<result_of_begin, t_ref>;
        using sentinel = detected_t<result_of_end, t_ref>;

        // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
        // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
        // but reimplementing these would be too much work, as a lot of other concepts are used underneath
        static constexpr auto is_iterator_begin =
                is_iterator_traits<iterator_traits<iterator>>::value;

    public:
        static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
    };

    template<typename R>
    using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R &>())>>;

    template<typename T>
    using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;

    // The following implementation of is_complete_type is taken from
    // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
    // and is written by Xiang Fan who agreed to using it in this library.

    template<typename T, typename = void>
    struct is_complete_type : std::false_type {};

    template<typename T>
    struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};

    template<typename BasicJsonType, typename CompatibleObjectType,
             typename = void>
    struct is_compatible_object_type_impl : std::false_type {};

    template<typename BasicJsonType, typename CompatibleObjectType>
    struct is_compatible_object_type_impl<
            BasicJsonType, CompatibleObjectType,
            enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value &&
                        is_detected<key_type_t, CompatibleObjectType>::value>> {
        using object_t = typename BasicJsonType::object_t;

        // macOS's is_constructible does not play well with nonesuch...
        static constexpr bool value =
                is_constructible<typename object_t::key_type,
                                 typename CompatibleObjectType::key_type>::value &&
                is_constructible<typename object_t::mapped_type,
                                 typename CompatibleObjectType::mapped_type>::value;
    };

    template<typename BasicJsonType, typename CompatibleObjectType>
    struct is_compatible_object_type
        : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};

    template<typename BasicJsonType, typename ConstructibleObjectType,
             typename = void>
    struct is_constructible_object_type_impl : std::false_type {};

    template<typename BasicJsonType, typename ConstructibleObjectType>
    struct is_constructible_object_type_impl<
            BasicJsonType, ConstructibleObjectType,
            enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value &&
                        is_detected<key_type_t, ConstructibleObjectType>::value>> {
        using object_t = typename BasicJsonType::object_t;

        static constexpr bool value =
                (is_default_constructible<ConstructibleObjectType>::value &&
                 (std::is_move_assignable<ConstructibleObjectType>::value ||
                  std::is_copy_assignable<ConstructibleObjectType>::value) &&
                 (is_constructible<typename ConstructibleObjectType::key_type,
                                   typename object_t::key_type>::value &&
                  std::is_same<
                          typename object_t::mapped_type,
                          typename ConstructibleObjectType::mapped_type>::value)) ||
                (has_from_json<BasicJsonType,
                               typename ConstructibleObjectType::mapped_type>::value ||
                 has_non_default_from_json<
                         BasicJsonType,
                         typename ConstructibleObjectType::mapped_type>::value);
    };

    template<typename BasicJsonType, typename ConstructibleObjectType>
    struct is_constructible_object_type
        : is_constructible_object_type_impl<BasicJsonType,
                                            ConstructibleObjectType> {};

    template<typename BasicJsonType, typename CompatibleStringType>
    struct is_compatible_string_type {
        static constexpr auto value =
                is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
    };

    template<typename BasicJsonType, typename ConstructibleStringType>
    struct is_constructible_string_type {
        // launder type through decltype() to fix compilation failure on ICPC
#ifdef __INTEL_COMPILER
        using laundered_type = decltype(std::declval<ConstructibleStringType>());
#else
        using laundered_type = ConstructibleStringType;
#endif

        static constexpr auto value =
                conjunction<
                        is_constructible<laundered_type, typename BasicJsonType::string_t>,
                        is_detected_exact<typename BasicJsonType::string_t::value_type,
                                          value_type_t, laundered_type>>::value;
    };

    template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
    struct is_compatible_array_type_impl : std::false_type {};

    template<typename BasicJsonType, typename CompatibleArrayType>
    struct is_compatible_array_type_impl<
            BasicJsonType, CompatibleArrayType,
            enable_if_t<
                    is_detected<iterator_t, CompatibleArrayType>::value &&
                    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value &&
                    // special case for types like std::filesystem::path whose iterator's value_type are themselves
                    // c.f. https://github.com/nlohmann/json/pull/3073
                    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value>> {
        static constexpr bool value =
                is_constructible<BasicJsonType,
                                 range_value_t<CompatibleArrayType>>::value;
    };

    template<typename BasicJsonType, typename CompatibleArrayType>
    struct is_compatible_array_type
        : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};

    template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
    struct is_constructible_array_type_impl : std::false_type {};

    template<typename BasicJsonType, typename ConstructibleArrayType>
    struct is_constructible_array_type_impl<
            BasicJsonType, ConstructibleArrayType,
            enable_if_t<std::is_same<ConstructibleArrayType,
                                     typename BasicJsonType::value_type>::value>>
        : std::true_type {};

    template<typename BasicJsonType, typename ConstructibleArrayType>
    struct is_constructible_array_type_impl<
            BasicJsonType, ConstructibleArrayType,
            enable_if_t<!std::is_same<ConstructibleArrayType,
                                      typename BasicJsonType::value_type>::value &&
                        !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value &&
                        is_default_constructible<ConstructibleArrayType>::value &&
                        (std::is_move_assignable<ConstructibleArrayType>::value ||
                         std::is_copy_assignable<ConstructibleArrayType>::value) &&
                        is_detected<iterator_t, ConstructibleArrayType>::value &&
                        is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value &&
                        is_detected<range_value_t, ConstructibleArrayType>::value &&
                        // special case for types like std::filesystem::path whose iterator's value_type are themselves
                        // c.f. https://github.com/nlohmann/json/pull/3073
                        !std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value &&
                        is_complete_type<
                                detected_t<range_value_t, ConstructibleArrayType>>::value>> {
        using value_type = range_value_t<ConstructibleArrayType>;

        static constexpr bool value =
                std::is_same<value_type,
                             typename BasicJsonType::array_t::value_type>::value ||
                has_from_json<BasicJsonType,
                              value_type>::value ||
                has_non_default_from_json<
                        BasicJsonType,
                        value_type>::value;
    };

    template<typename BasicJsonType, typename ConstructibleArrayType>
    struct is_constructible_array_type
        : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};

    template<typename RealIntegerType, typename CompatibleNumberIntegerType,
             typename = void>
    struct is_compatible_integer_type_impl : std::false_type {};

    template<typename RealIntegerType, typename CompatibleNumberIntegerType>
    struct is_compatible_integer_type_impl<
            RealIntegerType, CompatibleNumberIntegerType,
            enable_if_t<std::is_integral<RealIntegerType>::value &&
                        std::is_integral<CompatibleNumberIntegerType>::value &&
                        !std::is_same<bool, CompatibleNumberIntegerType>::value>> {
        // is there an assert somewhere on overflows?
        using RealLimits = std::numeric_limits<RealIntegerType>;
        using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;

        static constexpr auto value =
                is_constructible<RealIntegerType,
                                 CompatibleNumberIntegerType>::value &&
                CompatibleLimits::is_integer &&
                RealLimits::is_signed == CompatibleLimits::is_signed;
    };

    template<typename RealIntegerType, typename CompatibleNumberIntegerType>
    struct is_compatible_integer_type
        : is_compatible_integer_type_impl<RealIntegerType,
                                          CompatibleNumberIntegerType> {};

    template<typename BasicJsonType, typename CompatibleType, typename = void>
    struct is_compatible_type_impl : std::false_type {};

    template<typename BasicJsonType, typename CompatibleType>
    struct is_compatible_type_impl<
            BasicJsonType, CompatibleType,
            enable_if_t<is_complete_type<CompatibleType>::value>> {
        static constexpr bool value =
                has_to_json<BasicJsonType, CompatibleType>::value;
    };

    template<typename BasicJsonType, typename CompatibleType>
    struct is_compatible_type
        : is_compatible_type_impl<BasicJsonType, CompatibleType> {};

    template<typename T1, typename T2>
    struct is_constructible_tuple : std::false_type {};

    template<typename T1, typename... Args>
    struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};

    template<typename BasicJsonType, typename T>
    struct is_json_iterator_of : std::false_type {};

    template<typename BasicJsonType>
    struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};

    template<typename BasicJsonType>
    struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type {};

    // checks if a given type T is a template specialization of Primary
    template<template<typename...> class Primary, typename T>
    struct is_specialization_of : std::false_type {};

    template<template<typename...> class Primary, typename... Args>
    struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};

    template<typename T>
    using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;

    // checks if A and B are comparable using Compare functor
    template<typename Compare, typename A, typename B, typename = void>
    struct is_comparable : std::false_type {};

    template<typename Compare, typename A, typename B>
    struct is_comparable<Compare, A, B, void_t<decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())), decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))>> : std::true_type {};

    template<typename T>
    using detect_is_transparent = typename T::is_transparent;

    // type trait to check if KeyType can be used as object key (without a BasicJsonType)
    // see is_usable_as_basic_json_key_type below
    template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
             bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
    using is_usable_as_key_type = typename std::conditional<
            is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value && !(ExcludeObjectKeyType && std::is_same<KeyType, ObjectKeyType>::value) && (!RequireTransparentComparator || is_detected<detect_is_transparent, Comparator>::value) && !is_json_pointer<KeyType>::value,
            std::true_type,
            std::false_type>::type;

    // type trait to check if KeyType can be used as object key
    // true if:
    //   - KeyType is comparable with BasicJsonType::object_t::key_type
    //   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
    //   - the comparator is transparent or RequireTransparentComparator is false
    //   - KeyType is not a JSON iterator or json_pointer
    template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
             bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
    using is_usable_as_basic_json_key_type = typename std::conditional<
            is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
                                  typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
                                  RequireTransparentComparator, ExcludeObjectKeyType>::value &&
                    !is_json_iterator_of<BasicJsonType, KeyType>::value,
            std::true_type,
            std::false_type>::type;

    template<typename ObjectType, typename KeyType>
    using detect_erase_with_key_type = decltype(std::declval<ObjectType &>().erase(std::declval<KeyType>()));

    // type trait to check if object_t has an erase() member functions accepting KeyType
    template<typename BasicJsonType, typename KeyType>
    using has_erase_with_key_type = typename std::conditional<
            is_detected<
                    detect_erase_with_key_type,
                    typename BasicJsonType::object_t, KeyType>::value,
            std::true_type,
            std::false_type>::type;

    // a naive helper to check if a type is an ordered_map (exploits the fact that
    // ordered_map inherits capacity() from std::vector)
    template<typename T>
    struct is_ordered_map {
        using one = char;

        struct two {
            char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
        };

        template<typename C>
        static one test(decltype(&C::capacity));
        template<typename C>
        static two test(...);

        enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
    };

    // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
    template<typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
    T conditional_static_cast(U value) {
        return static_cast<T>(value);
    }

    template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
    T conditional_static_cast(U value) {
        return value;
    }

    template<typename... Types>
    using all_integral = conjunction<std::is_integral<Types>...>;

    template<typename... Types>
    using all_signed = conjunction<std::is_signed<Types>...>;

    template<typename... Types>
    using all_unsigned = conjunction<std::is_unsigned<Types>...>;

    // there's a disjunction trait in another PR; replace when merged
    template<typename... Types>
    using same_sign = std::integral_constant<bool,
                                             all_signed<Types...>::value || all_unsigned<Types...>::value>;

    template<typename OfType, typename T>
    using never_out_of_range = std::integral_constant<bool,
                                                      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType))) || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T))>;

    template<typename OfType, typename T,
             bool OfTypeSigned = std::is_signed<OfType>::value,
             bool TSigned = std::is_signed<T>::value>
    struct value_in_range_of_impl2;

    template<typename OfType, typename T>
    struct value_in_range_of_impl2<OfType, T, false, false> {
        static constexpr bool test(T val) {
            using CommonType = typename std::common_type<OfType, T>::type;
            return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
        }
    };

    template<typename OfType, typename T>
    struct value_in_range_of_impl2<OfType, T, true, false> {
        static constexpr bool test(T val) {
            using CommonType = typename std::common_type<OfType, T>::type;
            return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
        }
    };

    template<typename OfType, typename T>
    struct value_in_range_of_impl2<OfType, T, false, true> {
        static constexpr bool test(T val) {
            using CommonType = typename std::common_type<OfType, T>::type;
            return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
        }
    };


    template<typename OfType, typename T>
    struct value_in_range_of_impl2<OfType, T, true, true> {
        static constexpr bool test(T val) {
            using CommonType = typename std::common_type<OfType, T>::type;
            return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)()) && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
        }
    };

    template<typename OfType, typename T,
             bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
             typename = detail::enable_if_t<all_integral<OfType, T>::value>>
    struct value_in_range_of_impl1;

    template<typename OfType, typename T>
    struct value_in_range_of_impl1<OfType, T, false> {
        static constexpr bool test(T val) {
            return value_in_range_of_impl2<OfType, T>::test(val);
        }
    };

    template<typename OfType, typename T>
    struct value_in_range_of_impl1<OfType, T, true> {
        static constexpr bool test(T /*val*/) {
            return true;
        }
    };

    template<typename OfType, typename T>
    inline constexpr bool value_in_range_of(T val) {
        return value_in_range_of_impl1<OfType, T>::test(val);
    }

} // namespace detail
NLOHMANN_JSON_NAMESPACE_END
